package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.constant.CacheConstants;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.domain.vo.SkuLockVo;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.ISkuStockService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Package: com.spzx.product.service.impl
 *
 * @author xiongkunYang
 * @description:
 * @since 2025/6/7 8:24
 */
@Service
@RequiredArgsConstructor
public class SkuStockServiceImpl extends ServiceImpl<SkuStockMapper, SkuStock> implements ISkuStockService {

    private final RedisTemplate<Object, Object> redisTemplate;

    @Override
    public String checkAndLockStock(String orderNo, List<SkuLockVo> skuLockVoList) {
        StringBuffer errorMsg = new StringBuffer();
        // 分布式锁，防止表单重复提交的
        String lockKey = CacheConstants.SKU_CHECK_AND_LOCK_KEY + orderNo;
        String lockValue = UUID.randomUUID().toString();
        Boolean lock = this.redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.MINUTES);
        if (Boolean.FALSE.equals(lock)) {
            errorMsg.append("订单已提交");
            return errorMsg.toString();
        }
        try {

            // 遍历所有商品检查库存
            skuLockVoList.forEach(skuLockVo -> {
                int check = this.baseMapper.check(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
                if (check < 1) {
                    errorMsg.append("商品:").append(skuLockVo.getSkuId()).append("库存不足;");
                }
            });

            if (!errorMsg.isEmpty()) {
                return errorMsg.toString();
            }

            // 遍历所有商品锁定库存
            skuLockVoList.forEach(skuLockVo -> this.baseMapper.lock(skuLockVo.getSkuId(), skuLockVo.getSkuNum()));
            // 如果所有商品都锁定成功的情况下，需要缓存锁定信息到redis。以方便将来解锁库存 或者 减库存
            String dataKey = CacheConstants.SKU_LOCK_DATA + orderNo;
            this.redisTemplate.opsForValue().set(dataKey, skuLockVoList);

        } finally {
            // 释放分布式锁
            String script = """
                    if redis.call('get', KEYS[1]) == ARGV[1] then
                        return redis.call('del', KEYS[1])
                    else
                        return 0
                    end
                    """;
            redisTemplate.execute(
                    new DefaultRedisScript<>(script, Long.class),
                    List.of(lockKey),
                    lockValue
            );
        }
        return errorMsg.toString();
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void unlockStock(String orderNo) {

        //业务去重，防止重复消费
        String lock = CacheConstants.SKU_UNLOCK_KEY + orderNo;
        Boolean isFirst = redisTemplate.opsForValue().setIfAbsent(lock, orderNo, 1, TimeUnit.MINUTES);
        if(Boolean.FALSE.equals(isFirst)) {
            return;
        }

        // 获取锁定库存的缓存数据
        String dataKey = CacheConstants.SKU_LOCK_DATA + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)this.redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)){
            return ;
        }

        // 解锁库存
        skuLockVoList.forEach(skuLockVo -> {
            int row = this.baseMapper.unlock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if(row == 0) {
                // 解除去重
                this.redisTemplate.delete(lock);
                throw new ServiceException("解锁出库失败");
            }
        });

        // 解锁库存之后，删除锁定库存的缓存。以防止重复解锁库存
        this.redisTemplate.delete(dataKey);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void minusStock(String orderNo) {
        //业务去重，防止重复消费
        String lock = CacheConstants.SKU_MINUS_KEY + orderNo;
        Boolean isFirst = redisTemplate.opsForValue().setIfAbsent(lock, orderNo, 1, TimeUnit.MINUTES);
        if(Boolean.FALSE.equals(isFirst)) {
            return;
        }

        // 获取锁定库存的缓存数据
        String dataKey = CacheConstants.SKU_LOCK_DATA + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)this.redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)){
            return ;
        }

        // 扣减库存
        skuLockVoList.forEach(skuLockVo -> {
            int row = this.baseMapper.minus(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (row == 0) {
                // 解除去重
                this.redisTemplate.delete(lock);
                throw new ServiceException("扣减库存失败");
            }
        });

        // 扣减库存之后，删除锁定库存的缓存。以防止重复解锁库存
        this.redisTemplate.delete(dataKey);
    }
}